home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / sendmail / sendmail-5.65 / src / queue.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-05  |  24.5 KB  |  1,222 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted provided
  7.  * that: (1) source distributions retain this entire copyright notice and
  8.  * comment, and (2) distributions including binaries display the following
  9.  * acknowledgement:  ``This product includes software developed by the
  10.  * University of California, Berkeley and its contributors'' in the
  11.  * documentation or other materials provided with the distribution and in
  12.  * all advertising materials mentioning features or use of this software.
  13.  * Neither the name of the University nor the names of its contributors may
  14.  * be used to endorse or promote products derived from this software without
  15.  * specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  17.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  18.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  */
  20.  
  21. # include "sendmail.h"
  22.  
  23. #ifndef lint
  24. #ifdef QUEUE
  25. static char sccsid[] = "@(#)queue.c    5.30 (Berkeley) 6/1/90 (with queueing)";
  26. #else
  27. static char sccsid[] = "@(#)queue.c    5.30 (Berkeley) 6/1/90 (without queueing)";
  28. #endif
  29. #endif /* not lint */
  30.  
  31. # include <sys/stat.h>
  32. # include <sys/dir.h>
  33. # include <sys/file.h>
  34. # include <signal.h>
  35. # include <errno.h>
  36. # include <pwd.h>
  37.  
  38. # ifdef QUEUE
  39.  
  40. /*
  41. **  Work queue.
  42. */
  43.  
  44. struct work
  45. {
  46.     char        *w_name;    /* name of control file */
  47.     long        w_pri;        /* priority of message, see below */
  48.     time_t        w_ctime;    /* creation time of message */
  49.     struct work    *w_next;    /* next in queue */
  50. };
  51.  
  52. typedef struct work    WORK;
  53. extern int la;
  54.  
  55. WORK    *WorkQ;            /* queue of things to be done */
  56. /*
  57. **  QUEUEUP -- queue a message up for future transmission.
  58. **
  59. **    Parameters:
  60. **        e -- the envelope to queue up.
  61. **        queueall -- if TRUE, queue all addresses, rather than
  62. **            just those with the QQUEUEUP flag set.
  63. **        announce -- if TRUE, tell when you are queueing up.
  64. **
  65. **    Returns:
  66. **        locked FILE* to q file
  67. **
  68. **    Side Effects:
  69. **        The current request are saved in a control file.
  70. */
  71.  
  72. FILE *
  73. queueup(e, queueall, announce)
  74.     register ENVELOPE *e;
  75.     bool queueall;
  76.     bool announce;
  77. {
  78.     char *qf;
  79.     char buf[MAXLINE], tf[MAXLINE];
  80.     register FILE *tfp;
  81.     register HDR *h;
  82.     register ADDRESS *q;
  83.     MAILER nullmailer;
  84.     int fd, ret;
  85.  
  86.     /*
  87.     **  Create control file.
  88.     */
  89.  
  90.     do {
  91.         strcpy(tf, queuename(e, 't'));
  92.         fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
  93.         if (fd < 0) {
  94.             if ( errno != EEXIST) {
  95.                 syserr("queueup: cannot create temp file %s",
  96.                     tf);
  97.                 return NULL;
  98.             }
  99.         } else {
  100.             if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
  101.                 if (errno != EWOULDBLOCK)
  102.                     syserr("cannot flock(%s)", tf);
  103.                 close(fd);
  104.                 fd = -1;
  105.             }
  106.         }
  107.     } while (fd < 0);
  108.  
  109.     tfp = fdopen(fd, "w");
  110.  
  111.     if (tTd(40, 1))
  112.         printf("queueing %s\n", e->e_id);
  113.  
  114.     /*
  115.     **  If there is no data file yet, create one.
  116.     */
  117.  
  118.     if (e->e_df == NULL)
  119.     {
  120.         register FILE *dfp;
  121.         extern putbody();
  122.  
  123.         e->e_df = newstr(queuename(e, 'd'));
  124.         fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
  125.         if (fd < 0)
  126.         {
  127.             syserr("queueup: cannot create %s", e->e_df);
  128.             (void) fclose(tfp);
  129.             return NULL;
  130.         }
  131.         dfp = fdopen(fd, "w");
  132.         (*e->e_putbody)(dfp, ProgMailer, e);
  133.         (void) fclose(dfp);
  134.         e->e_putbody = putbody;
  135.     }
  136.  
  137.     /*
  138.     **  Output future work requests.
  139.     **    Priority and creation time should be first, since
  140.     **    they are required by orderq.
  141.     */
  142.  
  143.     /* output message priority */
  144.     fprintf(tfp, "P%ld\n", e->e_msgpriority);
  145.  
  146.     /* output creation time */
  147.     fprintf(tfp, "T%ld\n", e->e_ctime);
  148.  
  149.     /* output name of data file */
  150.     fprintf(tfp, "D%s\n", e->e_df);
  151.  
  152.     /* message from envelope, if it exists */
  153.     if (e->e_message != NULL)
  154.         fprintf(tfp, "M%s\n", e->e_message);
  155.  
  156.     /* output name of sender */
  157.     fprintf(tfp, "S%s\n", e->e_from.q_paddr);
  158.  
  159.     /* output list of recipient addresses */
  160.     for (q = e->e_sendqueue; q != NULL; q = q->q_next)
  161.     {
  162.         if (queueall ? !bitset(QDONTSEND, q->q_flags) :
  163.                    bitset(QQUEUEUP, q->q_flags))
  164.         {
  165.             char *ctluser, *getctluser();
  166.  
  167.             if ((ctluser = getctluser(q)) != NULL)
  168.                 fprintf(tfp, "C%s\n", ctluser);
  169.             fprintf(tfp, "R%s\n", q->q_paddr);
  170.             if (announce)
  171.             {
  172.                 e->e_to = q->q_paddr;
  173.                 message(Arpa_Info, "queued");
  174.                 if (LogLevel > 4)
  175.                     logdelivery("queued");
  176.                 e->e_to = NULL;
  177.             }
  178.             if (tTd(40, 1))
  179.             {
  180.                 printf("queueing ");
  181.                 printaddr(q, FALSE);
  182.             }
  183.         }
  184.     }
  185.  
  186.     /* output list of error recipients */
  187.     for (q = e->e_errorqueue; q != NULL; q = q->q_next)
  188.     {
  189.         if (!bitset(QDONTSEND, q->q_flags))
  190.         {
  191.             char *ctluser, *getctluser();
  192.  
  193.             if ((ctluser = getctluser(q)) != NULL)
  194.                 fprintf(tfp, "C%s\n", ctluser);
  195.             fprintf(tfp, "E%s\n", q->q_paddr);
  196.         }
  197.     }
  198.  
  199.     /*
  200.     **  Output headers for this message.
  201.     **    Expand macros completely here.  Queue run will deal with
  202.     **    everything as absolute headers.
  203.     **        All headers that must be relative to the recipient
  204.     **        can be cracked later.
  205.     **    We set up a "null mailer" -- i.e., a mailer that will have
  206.     **    no effect on the addresses as they are output.
  207.     */
  208.  
  209.     bzero((char *) &nullmailer, sizeof nullmailer);
  210.     nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
  211.     nullmailer.m_eol = "\n";
  212.  
  213.     define('g', "\001f", e);
  214.     for (h = e->e_header; h != NULL; h = h->h_link)
  215.     {
  216.         extern bool bitzerop();
  217.  
  218.         /* don't output null headers */
  219.         if (h->h_value == NULL || h->h_value[0] == '\0')
  220.             continue;
  221.  
  222.         /* don't output resent headers on non-resent messages */
  223.         if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
  224.             continue;
  225.  
  226.         /* output this header */
  227.         fprintf(tfp, "H");
  228.  
  229.         /* if conditional, output the set of conditions */
  230.         if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
  231.         {
  232.             int j;
  233.  
  234.             (void) putc('?', tfp);
  235.             for (j = '\0'; j <= '\177'; j++)
  236.                 if (bitnset(j, h->h_mflags))
  237.                     (void) putc(j, tfp);
  238.             (void) putc('?', tfp);
  239.         }
  240.  
  241.         /* output the header: expand macros, convert addresses */
  242.         if (bitset(H_DEFAULT, h->h_flags))
  243.         {
  244.             (void) expand(h->h_value, buf, &buf[sizeof buf], e);
  245.             fprintf(tfp, "%s: %s\n", h->h_field, buf);
  246.         }
  247.         else if (bitset(H_FROM|H_RCPT, h->h_flags))
  248.         {
  249.             commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
  250.                  &nullmailer);
  251.         }
  252.         else
  253.             fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
  254.     }
  255.  
  256.     /*
  257.     **  Clean up.
  258.     */
  259.  
  260.     qf = queuename(e, 'q');
  261.     if (rename(tf, qf) < 0)
  262.         syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
  263.     errno = 0;
  264.  
  265. # ifdef LOG
  266.     /* save log info */
  267.     if (LogLevel > 15)
  268.         syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
  269. # endif LOG
  270.     fflush(tfp);
  271.     return tfp;
  272. }
  273. /*
  274. **  RUNQUEUE -- run the jobs in the queue.
  275. **
  276. **    Gets the stuff out of the queue in some presumably logical
  277. **    order and processes them.
  278. **
  279. **    Parameters:
  280. **        forkflag -- TRUE if the queue scanning should be done in
  281. **            a child process.  We double-fork so it is not our
  282. **            child and we don't have to clean up after it.
  283. **
  284. **    Returns:
  285. **        none.
  286. **
  287. **    Side Effects:
  288. **        runs things in the mail queue.
  289. */
  290.  
  291. runqueue(forkflag)
  292.     bool forkflag;
  293. {
  294.     extern bool shouldqueue();
  295.  
  296.     /*
  297.     **  If no work will ever be selected, don't even bother reading
  298.     **  the queue.
  299.     */
  300.  
  301.     la = getla();    /* get load average */
  302.  
  303.     if (shouldqueue(-100000000L))
  304.     {
  305.         if (Verbose)
  306.             printf("Skipping queue run -- load average too high\n");
  307.  
  308.         if (forkflag)
  309.             return;
  310.         finis();
  311.     }
  312.  
  313.     /*
  314.     **  See if we want to go off and do other useful work.
  315.     */
  316.  
  317.     if (forkflag)
  318.     {
  319.         int pid;
  320.  
  321.         pid = dofork();
  322.         if (pid != 0)
  323.         {
  324.             extern reapchild();
  325.  
  326.             /* parent -- pick up intermediate zombie */
  327. #ifndef SIGCHLD
  328.             (void) waitfor(pid);
  329. #else SIGCHLD
  330.             (void) signal(SIGCHLD, reapchild);
  331. #endif SIGCHLD
  332.             if (QueueIntvl != 0)
  333.                 (void) setevent(QueueIntvl, runqueue, TRUE);
  334.             return;
  335.         }
  336.         /* child -- double fork */
  337. #ifndef SIGCHLD
  338.         if (fork() != 0)
  339.             exit(EX_OK);
  340. #else SIGCHLD
  341.         (void) signal(SIGCHLD, SIG_DFL);
  342. #endif SIGCHLD
  343.     }
  344.  
  345.     setproctitle("running queue: %s", QueueDir);
  346.  
  347. # ifdef LOG
  348.     if (LogLevel > 11)
  349.         syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
  350. # endif LOG
  351.  
  352.     /*
  353.     **  Release any resources used by the daemon code.
  354.     */
  355.  
  356. # ifdef DAEMON
  357.     clrdaemon();
  358. # endif DAEMON
  359.  
  360.     /*
  361.     **  Make sure the alias database is open.
  362.     */
  363.  
  364.     initaliases(AliasFile, FALSE);
  365.  
  366.     /*
  367.     **  Start making passes through the queue.
  368.     **    First, read and sort the entire queue.
  369.     **    Then, process the work in that order.
  370.     **        But if you take too long, start over.
  371.     */
  372.  
  373.     /* order the existing work requests */
  374.     (void) orderq(FALSE);
  375.  
  376.     /* process them once at a time */
  377.     while (WorkQ != NULL)
  378.     {
  379.         WORK *w = WorkQ;
  380.  
  381.         WorkQ = WorkQ->w_next;
  382.         dowork(w);
  383.         free(w->w_name);
  384.         free((char *) w);
  385.     }
  386.  
  387.     /* exit without the usual cleanup */
  388.     exit(ExitStat);
  389. }
  390. /*
  391. **  ORDERQ -- order the work queue.
  392. **
  393. **    Parameters:
  394. **        doall -- if set, include everything in the queue (even
  395. **            the jobs that cannot be run because the load
  396. **            average is too high).  Otherwise, exclude those
  397. **            jobs.
  398. **
  399. **    Returns:
  400. **        The number of request in the queue (not necessarily
  401. **        the number of requests in WorkQ however).
  402. **
  403. **    Side Effects:
  404. **        Sets WorkQ to the queue of available work, in order.
  405. */
  406.  
  407. # define NEED_P        001
  408. # define NEED_T        002
  409.  
  410. orderq(doall)
  411.     bool doall;
  412. {
  413.     register struct direct *d;
  414.     register WORK *w;
  415.     DIR *f;
  416.     register int i;
  417.     WORK wlist[QUEUESIZE+1];
  418.     int wn = -1;
  419.     extern workcmpf();
  420.  
  421.     /* clear out old WorkQ */
  422.     for (w = WorkQ; w != NULL; )
  423.     {
  424.         register WORK *nw = w->w_next;
  425.  
  426.         WorkQ = nw;
  427.         free(w->w_name);
  428.         free((char *) w);
  429.         w = nw;
  430.     }
  431.  
  432.     /* open the queue directory */
  433.     f = opendir(".");
  434.     if (f == NULL)
  435.     {
  436.         syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
  437.         return (0);
  438.     }
  439.  
  440.     /*
  441.     **  Read the work directory.
  442.     */
  443.  
  444.     while ((d = readdir(f)) != NULL)
  445.     {
  446.         FILE *cf;
  447.         char lbuf[MAXNAME];
  448.  
  449.         /* is this an interesting entry? */
  450.         if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
  451.             continue;
  452.  
  453.         /* yes -- open control file (if not too many files) */
  454.         if (++wn >= QUEUESIZE)
  455.             continue;
  456.         cf = fopen(d->d_name, "r");
  457.         if (cf == NULL)
  458.         {
  459.             /* this may be some random person sending hir msgs */
  460.             /* syserr("orderq: cannot open %s", cbuf); */
  461.             if (tTd(41, 2))
  462.                 printf("orderq: cannot open %s (%d)\n",
  463.                     d->d_name, errno);
  464.             errno = 0;
  465.             wn--;
  466.             continue;
  467.         }
  468.         w = &wlist[wn];
  469.         w->w_name = newstr(d->d_name);
  470.  
  471.         /* make sure jobs in creation don't clog queue */
  472.         w->w_pri = 0x7fffffff;
  473.         w->w_ctime = 0;
  474.  
  475.         /* extract useful information */
  476.         i = NEED_P | NEED_T;
  477.         while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
  478.         {
  479.             extern long atol();
  480.  
  481.             switch (lbuf[0])
  482.             {
  483.               case 'P':
  484.                 w->w_pri = atol(&lbuf[1]);
  485.                 i &= ~NEED_P;
  486.                 break;
  487.  
  488.               case 'T':
  489.                 w->w_ctime = atol(&lbuf[1]);
  490.                 i &= ~NEED_T;
  491.                 break;
  492.             }
  493.         }
  494.         (void) fclose(cf);
  495.  
  496.         if (!doall && shouldqueue(w->w_pri))
  497.         {
  498.             /* don't even bother sorting this job in */
  499.             wn--;
  500.         }
  501.     }
  502.     (void) closedir(f);
  503.     wn++;
  504.  
  505.     /*
  506.     **  Sort the work directory.
  507.     */
  508.  
  509.     qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
  510.  
  511.     /*
  512.     **  Convert the work list into canonical form.
  513.     **    Should be turning it into a list of envelopes here perhaps.
  514.     */
  515.  
  516.     WorkQ = NULL;
  517.     for (i = min(wn, QUEUESIZE); --i >= 0; )
  518.     {
  519.         w = (WORK *) xalloc(sizeof *w);
  520.         w->w_name = wlist[i].w_name;
  521.         w->w_pri = wlist[i].w_pri;
  522.         w->w_ctime = wlist[i].w_ctime;
  523.         w->w_next = WorkQ;
  524.         WorkQ = w;
  525.     }
  526.  
  527.     if (tTd(40, 1))
  528.     {
  529.         for (w = WorkQ; w != NULL; w = w->w_next)
  530.             printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
  531.     }
  532.  
  533.     return (wn);
  534. }
  535. /*
  536. **  WORKCMPF -- compare function for ordering work.
  537. **
  538. **    Parameters:
  539. **        a -- the first argument.
  540. **        b -- the second argument.
  541. **
  542. **    Returns:
  543. **        -1 if a < b
  544. **         0 if a == b
  545. **        +1 if a > b
  546. **
  547. **    Side Effects:
  548. **        none.
  549. */
  550.  
  551. workcmpf(a, b)
  552.     register WORK *a;
  553.     register WORK *b;
  554. {
  555.     long pa = a->w_pri + a->w_ctime;
  556.     long pb = b->w_pri + b->w_ctime;
  557.  
  558.     if (pa == pb)
  559.         return (0);
  560.     else if (pa > pb)
  561.         return (1);
  562.     else
  563.         return (-1);
  564. }
  565. /*
  566. **  DOWORK -- do a work request.
  567. **
  568. **    Parameters:
  569. **        w -- the work request to be satisfied.
  570. **
  571. **    Returns:
  572. **        none.
  573. **
  574. **    Side Effects:
  575. **        The work request is satisfied if possible.
  576. */
  577.  
  578. dowork(w)
  579.     register WORK *w;
  580. {
  581.     register int i;
  582.     extern bool shouldqueue();
  583.  
  584.     if (tTd(40, 1))
  585.         printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
  586.  
  587.     /*
  588.     **  Ignore jobs that are too expensive for the moment.
  589.     */
  590.  
  591.     if (shouldqueue(w->w_pri))
  592.     {
  593.         if (Verbose)
  594.             printf("\nSkipping %s\n", w->w_name + 2);
  595.         return;
  596.     }
  597.  
  598.     /*
  599.     **  Fork for work.
  600.     */
  601.  
  602.     if (ForkQueueRuns)
  603.     {
  604.         i = fork();
  605.         if (i < 0)
  606.         {
  607.             syserr("dowork: cannot fork");
  608.             return;
  609.         }
  610.     }
  611.     else
  612.     {
  613.         i = 0;
  614.     }
  615.  
  616.     if (i == 0)
  617.     {
  618.         FILE *qflock, *readqf();
  619.         /*
  620.         **  CHILD
  621.         **    Lock the control file to avoid duplicate deliveries.
  622.         **        Then run the file as though we had just read it.
  623.         **    We save an idea of the temporary name so we
  624.         **        can recover on interrupt.
  625.         */
  626.  
  627.         /* set basic modes, etc. */
  628.         (void) alarm(0);
  629.         clearenvelope(CurEnv, FALSE);
  630.         QueueRun = TRUE;
  631.         ErrorMode = EM_MAIL;
  632.         CurEnv->e_id = &w->w_name[2];
  633. # ifdef LOG
  634.         if (LogLevel > 11)
  635.             syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
  636.                    getpid());
  637. # endif LOG
  638.  
  639.         /* don't use the headers from sendmail.cf... */
  640.         CurEnv->e_header = NULL;
  641.  
  642.         /* read the queue control file */
  643.         /*  and lock the control file during processing */
  644.         if ((qflock=readqf(CurEnv, TRUE)) == NULL)
  645.         {
  646.             if (ForkQueueRuns)
  647.                 exit(EX_OK);
  648.             else
  649.                 return;
  650.         }
  651.  
  652.         CurEnv->e_flags |= EF_INQUEUE;
  653.         eatheader(CurEnv);
  654.  
  655.         /* do the delivery */
  656.         if (!bitset(EF_FATALERRS, CurEnv->e_flags))
  657.             sendall(CurEnv, SM_DELIVER);
  658.  
  659.         /* finish up and exit */
  660.         if (ForkQueueRuns)
  661.             finis();
  662.         else
  663.             dropenvelope(CurEnv);
  664.         fclose(qflock);
  665.     }
  666.     else
  667.     {
  668.         /*
  669.         **  Parent -- pick up results.
  670.         */
  671.  
  672.         errno = 0;
  673.         (void) waitfor(i);
  674.     }
  675. }
  676. /*
  677. **  READQF -- read queue file and set up environment.
  678. **
  679. **    Parameters:
  680. **        e -- the envelope of the job to run.
  681. **        full -- if set, read in all information.  Otherwise just
  682. **            read in info needed for a queue print.
  683. **
  684. **    Returns:
  685. **        FILE * pointing to flock()ed fd so it can be closed
  686. **        after the mail is delivered
  687. **
  688. **    Side Effects:
  689. **        cf is read and created as the current job, as though
  690. **        we had been invoked by argument.
  691. */
  692.  
  693. FILE *
  694. readqf(e, full)
  695.     register ENVELOPE *e;
  696.     bool full;
  697. {
  698.     char *qf;
  699.     register FILE *qfp;
  700.     char buf[MAXFIELD];
  701.     extern char *fgetfolded();
  702.     extern long atol();
  703.     int gotctluser = 0;
  704.     int fd;
  705.  
  706.     /*
  707.     **  Read and process the file.
  708.     */
  709.  
  710.     qf = queuename(e, 'q');
  711.     qfp = fopen(qf, "r");
  712.     if (qfp == NULL)
  713.     {
  714.         if (errno != ENOENT)
  715.             syserr("readqf: no control file %s", qf);
  716.         return NULL;
  717.     }
  718.  
  719.     if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0)
  720.     {
  721. # ifdef LOG
  722.         /* being processed by another queuer */
  723.         if (Verbose)
  724.             printf("%s: locked\n", CurEnv->e_id);
  725. # endif LOG
  726.         (void) fclose(qfp);
  727.         return NULL;
  728.     }
  729.  
  730.     /* do basic system initialization */
  731.     initsys();
  732.  
  733.     FileName = qf;
  734.     LineNumber = 0;
  735.     if (Verbose && full)
  736.         printf("\nRunning %s\n", e->e_id);
  737.     while (fgetfolded(buf, sizeof buf, qfp) != NULL)
  738.     {
  739.         if (tTd(40, 4))
  740.             printf("+++++ %s\n", buf);
  741.         switch (buf[0])
  742.         {
  743.           case 'C':        /* specify controlling user */
  744.             setctluser(&buf[1]);
  745.             gotctluser = 1;
  746.             break;
  747.  
  748.           case 'R':        /* specify recipient */
  749.             sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
  750.             break;
  751.  
  752.           case 'E':        /* specify error recipient */
  753.             sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue);
  754.             break;
  755.  
  756.           case 'H':        /* header */
  757.             if (full)
  758.                 (void) chompheader(&buf[1], FALSE);
  759.             break;
  760.  
  761.           case 'M':        /* message */
  762.             e->e_message = newstr(&buf[1]);
  763.             break;
  764.  
  765.           case 'S':        /* sender */
  766.             setsender(newstr(&buf[1]));
  767.             break;
  768.  
  769.           case 'D':        /* data file name */
  770.             if (!full)
  771.                 break;
  772.             e->e_df = newstr(&buf[1]);
  773.             e->e_dfp = fopen(e->e_df, "r");
  774.             if (e->e_dfp == NULL)
  775.                 syserr("readqf: cannot open %s", e->e_df);
  776.             break;
  777.  
  778.           case 'T':        /* init time */
  779.             e->e_ctime = atol(&buf[1]);
  780.             break;
  781.  
  782.           case 'P':        /* message priority */
  783.             e->e_msgpriority = atol(&buf[1]) + WkTimeFact;
  784.             break;
  785.  
  786.           case '\0':        /* blank line; ignore */
  787.             break;
  788.  
  789.           default:
  790.             syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
  791.                 LineNumber, buf);
  792.             break;
  793.         }
  794.         /*
  795.         **  The `C' queue file command operates on the next line,
  796.         **  so we use "gotctluser" to maintain state as follows:
  797.         **      0 - no controlling user,
  798.         **      1 - controlling user has been set but not used,
  799.         **      2 - controlling user must be used on next iteration.
  800.         */
  801.         if (gotctluser == 1)
  802.             gotctluser++;
  803.         else if (gotctluser == 2)
  804.         {
  805.             clrctluser();
  806.             gotctluser = 0;
  807.         }
  808.     }
  809.  
  810.     /* clear controlling user in case we break out prematurely */
  811.     clrctluser();
  812.  
  813.     FileName = NULL;
  814.  
  815.     /*
  816.     **  If we haven't read any lines, this queue file is empty.
  817.     **  Arrange to remove it without referencing any null pointers.
  818.     */
  819.  
  820.     if (LineNumber == 0)
  821.     {
  822.         errno = 0;
  823.         e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
  824.     }
  825.     return qfp;
  826. }
  827. /*
  828. **  PRINTQUEUE -- print out a representation of the mail queue
  829. **
  830. **    Parameters:
  831. **        none.
  832. **
  833. **    Returns:
  834. **        none.
  835. **
  836. **    Side Effects:
  837. **        Prints a listing of the mail queue on the standard output.
  838. */
  839.  
  840. printqueue()
  841. {
  842.     register WORK *w;
  843.     FILE *f;
  844.     int nrequests;
  845.     char buf[MAXLINE];
  846.     char cbuf[MAXLINE];
  847.  
  848.     /*
  849.     **  Read and order the queue.
  850.     */
  851.  
  852.     nrequests = orderq(TRUE);
  853.  
  854.     /*
  855.     **  Print the work list that we have read.
  856.     */
  857.  
  858.     /* first see if there is anything */
  859.     if (nrequests <= 0)
  860.     {
  861.         printf("Mail queue is empty\n");
  862.         return;
  863.     }
  864.  
  865.     la = getla();    /* get load average */
  866.  
  867.     printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
  868.     if (nrequests > QUEUESIZE)
  869.         printf(", only %d printed", QUEUESIZE);
  870.     if (Verbose)
  871.         printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
  872.     else
  873.         printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
  874.     for (w = WorkQ; w != NULL; w = w->w_next)
  875.     {
  876.         struct stat st;
  877.         auto time_t submittime = 0;
  878.         long dfsize = -1;
  879.         char message[MAXLINE];
  880.         extern bool shouldqueue();
  881.  
  882.         f = fopen(w->w_name, "r");
  883.         if (f == NULL)
  884.         {
  885.             errno = 0;
  886.             continue;
  887.         }
  888.         printf("%7s", w->w_name + 2);
  889.         if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0)
  890.             printf("*");
  891.         else if (shouldqueue(w->w_pri))
  892.             printf("X");
  893.         else
  894.             printf(" ");
  895.         errno = 0;
  896.  
  897.         message[0] = '\0';
  898.         cbuf[0] = '\0';
  899.         while (fgets(buf, sizeof buf, f) != NULL)
  900.         {
  901.             fixcrlf(buf, TRUE);
  902.             switch (buf[0])
  903.             {
  904.               case 'M':    /* error message */
  905.                 (void) strcpy(message, &buf[1]);
  906.                 break;
  907.  
  908.               case 'S':    /* sender name */
  909.                 if (Verbose)
  910.                     printf("%8ld %10ld %.12s %.38s", dfsize,
  911.                         w->w_pri, ctime(&submittime) + 4,
  912.                         &buf[1]);
  913.                 else
  914.                     printf("%8ld %.16s %.45s", dfsize,
  915.                         ctime(&submittime), &buf[1]);
  916.                 if (message[0] != '\0')
  917.                     printf("\n\t\t (%.60s)", message);
  918.                 break;
  919.               case 'C':    /* controlling user */
  920.                 if (strlen(buf) < MAXLINE-3)    /* sanity */
  921.                     (void) strcat(buf, ") ");
  922.                 cbuf[0] = cbuf[1] = '(';
  923.                 (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1);
  924.                 cbuf[MAXLINE-1] = '\0';
  925.                 break;
  926.  
  927.               case 'R':    /* recipient name */
  928.                 if (cbuf[0] != '\0') {
  929.                     /* prepend controlling user to `buf' */
  930.                     (void) strncat(cbuf, &buf[1],
  931.                                   MAXLINE-strlen(cbuf));
  932.                     cbuf[MAXLINE-1] = '\0';
  933.                     (void) strcpy(buf, cbuf);
  934.                     cbuf[0] = '\0';
  935.                 }
  936.                 if (Verbose)
  937.                     printf("\n\t\t\t\t\t %.38s", &buf[1]);
  938.                 else
  939.                     printf("\n\t\t\t\t  %.45s", &buf[1]);
  940.                 break;
  941.  
  942.               case 'T':    /* creation time */
  943.                 submittime = atol(&buf[1]);
  944.                 break;
  945.  
  946.               case 'D':    /* data file name */
  947.                 if (stat(&buf[1], &st) >= 0)
  948.                     dfsize = st.st_size;
  949.                 break;
  950.             }
  951.         }
  952.         if (submittime == (time_t) 0)
  953.             printf(" (no control file)");
  954.         printf("\n");
  955.         (void) fclose(f);
  956.     }
  957. }
  958.  
  959. # endif QUEUE
  960. /*
  961. **  QUEUENAME -- build a file name in the queue directory for this envelope.
  962. **
  963. **    Assigns an id code if one does not already exist.
  964. **    This code is very careful to avoid trashing existing files
  965. **    under any circumstances.
  966. **
  967. **    Parameters:
  968. **        e -- envelope to build it in/from.
  969. **        type -- the file type, used as the first character
  970. **            of the file name.
  971. **
  972. **    Returns:
  973. **        a pointer to the new file name (in a static buffer).
  974. **
  975. **    Side Effects:
  976. **        Will create the qf file if no id code is
  977. **        already assigned.  This will cause the envelope
  978. **        to be modified.
  979. */
  980.  
  981. char *
  982. queuename(e, type)
  983.     register ENVELOPE *e;
  984.     char type;
  985. {
  986.     static char buf[MAXNAME];
  987.     static int pid = -1;
  988.     char c1 = 'A';
  989.     char c2 = 'A';
  990.  
  991.     if (e->e_id == NULL)
  992.     {
  993.         char qf[20];
  994.  
  995.         /* find a unique id */
  996.         if (pid != getpid())
  997.         {
  998.             /* new process -- start back at "AA" */
  999.             pid = getpid();
  1000.             c1 = 'A';
  1001.             c2 = 'A' - 1;
  1002.         }
  1003.         (void) sprintf(qf, "qfAA%05d", pid);
  1004.  
  1005.         while (c1 < '~' || c2 < 'Z')
  1006.         {
  1007.             int i;
  1008.  
  1009.             if (c2 >= 'Z')
  1010.             {
  1011.                 c1++;
  1012.                 c2 = 'A' - 1;
  1013.             }
  1014.             qf[2] = c1;
  1015.             qf[3] = ++c2;
  1016.             if (tTd(7, 20))
  1017.                 printf("queuename: trying \"%s\"\n", qf);
  1018.  
  1019.             i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
  1020.             if (i < 0) {
  1021.                 if (errno != EEXIST) {
  1022.                     syserr("queuename: Cannot create \"%s\" in \"%s\"",
  1023.                         qf, QueueDir);
  1024.                     exit(EX_UNAVAILABLE);
  1025.                 }
  1026.             } else {
  1027.                 (void) close(i);
  1028.                 break;
  1029.             }
  1030.         }
  1031.         if (c1 >= '~' && c2 >= 'Z')
  1032.         {
  1033.             syserr("queuename: Cannot create \"%s\" in \"%s\"",
  1034.                 qf, QueueDir);
  1035.             exit(EX_OSERR);
  1036.         }
  1037.         e->e_id = newstr(&qf[2]);
  1038.         define('i', e->e_id, e);
  1039.         if (tTd(7, 1))
  1040.             printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
  1041. # ifdef LOG
  1042.         if (LogLevel > 16)
  1043.             syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
  1044. # endif LOG
  1045.     }
  1046.  
  1047.     if (type == '\0')
  1048.         return (NULL);
  1049.     (void) sprintf(buf, "%cf%s", type, e->e_id);
  1050.     if (tTd(7, 2))
  1051.         printf("queuename: %s\n", buf);
  1052.     return (buf);
  1053. }
  1054. /*
  1055. **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
  1056. **
  1057. **    Parameters:
  1058. **        e -- the envelope to unlock.
  1059. **
  1060. **    Returns:
  1061. **        none
  1062. **
  1063. **    Side Effects:
  1064. **        unlocks the queue for `e'.
  1065. */
  1066.  
  1067. unlockqueue(e)
  1068.     ENVELOPE *e;
  1069. {
  1070.     /* remove the transcript */
  1071. # ifdef LOG
  1072.     if (LogLevel > 19)
  1073.         syslog(LOG_DEBUG, "%s: unlock", e->e_id);
  1074. # endif LOG
  1075.     if (!tTd(51, 4))
  1076.         xunlink(queuename(e, 'x'));
  1077.  
  1078. }
  1079. /*
  1080. **  GETCTLUSER -- return controlling user if mailing to prog or file
  1081. **
  1082. **    Check for a "|" or "/" at the beginning of the address.  If
  1083. **    found, return a controlling username.
  1084. **
  1085. **    Parameters:
  1086. **        a - the address to check out
  1087. **
  1088. **    Returns:
  1089. **        Either NULL, if we werent mailing to a program or file,
  1090. **        or a controlling user name (possibly in getpwuid's
  1091. **        static buffer).
  1092. **
  1093. **    Side Effects:
  1094. **        none.
  1095. */
  1096.  
  1097. char *
  1098. getctluser(a)
  1099.     ADDRESS *a;
  1100. {
  1101.     extern ADDRESS *getctladdr();
  1102.     struct passwd *pw;
  1103.     char *retstr;
  1104.  
  1105.     /*
  1106.     **  Get unquoted user for file, program or user.name check.
  1107.     **  N.B. remove this code block to always emit controlling
  1108.     **  addresses (at the expense of backward compatibility).
  1109.     */
  1110.  
  1111.     {
  1112.         char buf[MAXNAME];
  1113.         (void) strncpy(buf, a->q_paddr, MAXNAME);
  1114.         buf[MAXNAME-1] = '\0';
  1115.         stripquotes(buf, TRUE);
  1116.  
  1117.         if (buf[0] != '|' && buf[0] != '/')
  1118.             return((char *)NULL);
  1119.     }
  1120.  
  1121.     a = getctladdr(a);        /* find controlling address */
  1122.  
  1123.     if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL)
  1124.         retstr = pw->pw_name;
  1125.     else                /* use default user */
  1126.         retstr = DefUser;
  1127.  
  1128.     if (tTd(40, 5))
  1129.         printf("Set controlling user for `%s' to `%s'\n",
  1130.                (a == NULL)? "<null>": a->q_paddr, retstr);
  1131.  
  1132.     return(retstr);
  1133. }
  1134. /*
  1135. **  SETCTLUSER - sets `CtlUser' to controlling user
  1136. **  CLRCTLUSER - clears controlling user (no params, nothing returned)
  1137. **
  1138. **    These routines manipulate `CtlUser'.
  1139. **
  1140. **    Parameters:
  1141. **        str  - controlling user as passed to setctluser()
  1142. **
  1143. **    Returns:
  1144. **        None.
  1145. **
  1146. **    Side Effects:
  1147. **        `CtlUser' is changed.
  1148. */
  1149.  
  1150. static char CtlUser[MAXNAME];
  1151.  
  1152. setctluser(str)
  1153. register char *str;
  1154. {
  1155.     (void) strncpy(CtlUser, str, MAXNAME);
  1156.     CtlUser[MAXNAME-1] = '\0';
  1157. }
  1158.  
  1159. clrctluser()
  1160. {
  1161.     CtlUser[0] = '\0';
  1162. }
  1163.  
  1164. /*
  1165. **  SETCTLADDR -- create a controlling address
  1166. **
  1167. **    If global variable `CtlUser' is set and we are given a valid
  1168. **    address, make that address a controlling address; change the
  1169. **    `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID.
  1170. **
  1171. **    Parameters:
  1172. **        a - address for which control uid/gid info may apply
  1173. **
  1174. **    Returns:
  1175. **        None.    
  1176. **
  1177. **    Side Effects:
  1178. **        Fills in uid/gid fields in address and sets QGOODUID
  1179. **        flag if appropriate.
  1180. */
  1181.  
  1182. setctladdr(a)
  1183.     ADDRESS *a;
  1184. {
  1185.     struct passwd *pw;
  1186.  
  1187.     /*
  1188.     **  If there is no current controlling user, or we were passed a
  1189.     **  NULL addr ptr or we already have a controlling user, return.
  1190.     */
  1191.  
  1192.     if (CtlUser[0] == '\0' || a == NULL || a->q_ruser)
  1193.         return;
  1194.  
  1195.     /*
  1196.     **  Set up addr fields for controlling user.  If `CtlUser' is no
  1197.     **  longer valid, use the default user/group.
  1198.     */
  1199.  
  1200.     if ((pw = getpwnam(CtlUser)) != NULL)
  1201.     {
  1202.         if (a->q_home)
  1203.             free(a->q_home);
  1204.         a->q_home = newstr(pw->pw_dir);
  1205.         a->q_uid = pw->pw_uid;
  1206.         a->q_gid = pw->pw_gid;
  1207.         a->q_ruser = newstr(CtlUser);
  1208.     }
  1209.     else
  1210.     {
  1211.         a->q_uid = DefUid;
  1212.         a->q_gid = DefGid;
  1213.         a->q_ruser = newstr(DefUser);
  1214.     }
  1215.  
  1216.     a->q_flags |= QGOODUID;        /* flag as a "ctladdr"  */
  1217.  
  1218.     if (tTd(40, 5))
  1219.         printf("Restored controlling user for `%s' to `%s'\n",
  1220.                a->q_paddr, a->q_ruser);
  1221. }
  1222.